package com.wn.sk.gateway.filter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wn.common.api.Result;
import com.wn.common.api.status.impl.ResultCode;
import com.wn.sk.common.jwt.JwtTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.UnknownFormatConversionException;
import java.util.concurrent.TimeUnit;

/**
 * @author yellow Docter
 * @date 2022 -09-07
 * @desc
      案例：
        对请求：/promotion-seckill/auth/order  对所有的auth的路劲进行鉴权操作

        01 拿到请求,进行判断那些 path需要鉴权
        02 拿到请求头中的携带的：Authorization 中的token
        03 token的校验
        04 拿到token中值
        05 和redis中存储的token  判断过期没有

 */
@Component
@Slf4j
public class AuthFilter implements GlobalFilter, Ordered {

    @Autowired
    private JwtTemplate jwtTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    //ServerWebExchange 服务器交换机 这里封装了 请求、响应、路径等内容
    // gateway 中使用webflux 内容
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        //请求对象 、响应对象
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        //01 拿到请求,进行判断那些 path需要鉴权
        String path = exchange.getRequest().getURI().getPath();
        //判断 对那些path 进行鉴权
        //promotion-seckill/auth/order     鉴权
        //promotion-seckill/authaaa/order  不能鉴权
        // path.contains("auth"); //进行鉴权 (字符可以使用正则处理 )
        // AntPathMatcher 封装了路劲的一些判断
        AntPathMatcher pathMatcher = new AntPathMatcher();
        //请求中包含了 auth 进行鉴权
        boolean match = pathMatcher.match("/**/auth/**", path);
        if (match){
            //进行鉴权
            //02 拿到请求头中的携带的：Authorization 中的token
            String token = request.getHeaders().getFirst("Authorization");
            log.info("拿到请求头中的token:{}",token);
            if (StringUtils.isEmpty(token)){
                log.error("拿到请求头中的token为空...");
                return error(response);
            }
            //03 token进行校验
            token = token.replace("Bearer ","");
            //进行校验

            //false失败
            boolean verifyToken = jwtTemplate.verifyToken(token);

            if (!verifyToken){
                log.error("token的校验失败...");
                return error(response);
            }

            // 04 解析token
            String userId = (String) jwtTemplate.parseToken(token, "userId");
            System.out.println("userId = " + userId);

            //05和redis中存储的token  判断过期没有
            String tokenKey = "sk:member:" + userId;
            String redisToken = stringRedisTemplate.opsForValue().get(tokenKey);
            if (StringUtils.isEmpty(redisToken)){
                log.error("token时效过期了....");
                return error(response);
            }

            //06  重写头信息
            ServerHttpRequest.Builder mutate = request.mutate();
            mutate.header("userId",userId);

            //07 redis中的存储 的token 进行续签
            stringRedisTemplate.expire(tokenKey,
                    30,
                    TimeUnit.MINUTES
                    );
        }
        return chain.filter(exchange);
    }

    /**
     * 出现异常 进行回写
     * @param response
     * @return
     */
    private Mono<Void> error(ServerHttpResponse response) {
        //进行回写
        ResultCode permissionNoAccess = ResultCode.PERMISSION_NO_ACCESS;

        Result<Object> error = Result.error(permissionNoAccess);
        log.info("回写信息,{}",error);

        ObjectMapper objectMapper = new ObjectMapper();
        byte[] bytes =null;
        try {
            bytes= objectMapper.writeValueAsBytes(error);  //拿到错误信息转成byte数组
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        DataBuffer wrap = response.bufferFactory().wrap(bytes);
        return response.writeWith(Mono.just(wrap));
    }

    @Override
    public int getOrder() {
        return 0;
    }
}
